<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div>


        <select id="eraser-width-select">
            <option value=25>25</option>
            <option value=50>50</option>
            <option value=75>75</option>
            <option value=100>100</option>
            <option value=125>125</option>
        </select>
    </div>
    <canvas id="my-canvas" width="500" height="500" style="border: solid 1px black">

    </canvas>
</body>

</html>

<script>

    const
        canvas = document.getElementById('my-canvas'),
        ctx = canvas.getContext('2d'),
        eraserWidthSelect = document.getElementById('eraser-width-select');

    const
        ERASER_LINE_WIDTH = 1,
        ERASER_STROKE_STYLE = 'rgb(0,0,255)';



    let
        lastX, //标记最后的X位置，用于ctx.clip()剪辑及mouseup时去除橡皮擦的图形
        lastY,//同上，记录y位置
        imageData = null,
        mouseDown = { x: null, y: null },
        rubberBandRect = { left: null, top: null, width: null, height: null },
        dragging = false;
    ctx.fillStyle = '#f5f5f5';
    ctx.fillRect(0, 0, canvas.width, canvas.height);



    function windowToCanvas(x, y) {
        var bbox = canvas.getBoundingClientRect();
        return {
            x: x - bbox.left * (canvas.width / bbox.width),
            y: y - bbox.top * (canvas.height / bbox.height)
        };
    }

    function restoreDrawingSurface() {
        ctx.putImageData(imageData, 0, 0);
    }



    function eraseLast() {
        ctx.save();

        setErasePathForEraser();//设置擦除路径
        // 👆setErasePathForEraser()方法中的ctx.clip()指定了剪辑区域，下面的效果只会在剪辑区域里发生
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)

        ctx.restore();
    }

    function setErasePathForEraser() {
        const eraserWidth = parseFloat(eraserWidthSelect.value);

        ctx.beginPath();

        ctx.arc(lastX, lastY,
            eraserWidth / 2 + ERASER_LINE_WIDTH,
            0, Math.PI * 2, false);

        // 裁剪，设置绘图区域
        ctx.clip();
    }

    function setEraserAttributes() {
        ctx.lineWidth = ERASER_LINE_WIDTH;

        ctx.strokeStyle = ERASER_STROKE_STYLE;
    }

    function setDrawPathForEraser(loc) {
        const eraserWidth = parseFloat(eraserWidthSelect.value);

        ctx.beginPath();

        ctx.arc(
            loc.x,
            loc.y,
            eraserWidth / 2
            , 0, Math.PI * 2, false)

        ctx.clip();

    }

    function drawEraser(loc) {
        ctx.save();

        setEraserAttributes();
        setDrawPathForEraser(loc);
        ctx.stroke();

        ctx.restore();
    }

    canvas.onmousedown = function (e) {
        const loc = windowToCanvas(e.clientX, e.clientY);

        e.preventDefault();

        mouseDown.x = loc.x;
        mouseDown.y = loc.y;

        // 记录位置
        lastX = loc.x;
        lastY = loc.y;

        dragging = true;

    }

    canvas.onmousemove = function (e) {
        let loc;

        if (dragging) {
            e.preventDefault();

            loc = windowToCanvas(e.clientX, e.clientY);


            eraseLast();  //擦除lastX、lastY所记录位置的图形
            drawEraser(loc); //画橡皮擦图形

            //更新lastX与lastY
            lastX = loc.x;
            lastY = loc.y;
        }
    }

    canvas.onmouseup = function (e) {
        const loc = windowToCanvas(e.clientX, e.clientY);



        // 最后清除橡皮擦图形及mousedown区域（如果没有触发mousemove
        eraseLast();

        dragging = false;

    }

</script>